use {
    crate::{base::client::Client, service::{block, common}}, anychain_tron::TronAddress, repository::model::{
        dapp_block_scan_progress::DAppBlockScanProgress, dapp_transaction::DAppTransaction,
    }, 
    alloy::primitives::U256,
    std::str::FromStr, sqlx::{MySql, Pool}, tracing::{debug, info},
};

const METHOD_ID_TRANSFER: &str = "a9059cbb";
const METHOD_ID_TRANSFER_FROM: &str = "23b872dd";

const TO_BE_CONFIRMED: u64 = 20;

pub async fn listen_token(
    client: &Client,
    pool: &Pool<MySql>,
    contract_vec: &Vec<String>,
    account_vec: &Vec<String>,
    current_block_number: u64,
    scan_progress_id: u64,
) -> anyhow::Result<()> {
    let mut next_number = current_block_number - TO_BE_CONFIRMED;
    let mut dapp_txs = Vec::new();
    let step = TO_BE_CONFIRMED + 60;

    let now_block = block::get_now_block(client).await?;
    let now_block_number = now_block.block_header.raw_data.number;
    let until_block_number = now_block_number - TO_BE_CONFIRMED;

    debug!("block num: {}", current_block_number);
    for _ in 0..step {
        if until_block_number <= next_number {
            break;
        }
        let next_block = block::get_block_by_num(client, next_number).await?;
        let time_stamp = next_block.block_header.raw_data.timestamp;
        for contract_str in contract_vec {
            let txs = next_block.transactions.iter();
            for tx in txs {
                let tx_id = tx.tx_id.clone();
                let tx_contract_vec = tx.raw_data.contract.clone();
                for tx_contract in tx_contract_vec {
                    let parameter_value = tx_contract.parameter.value;
                    let tx_contract_opt = parameter_value.contract_address.clone();
                    let tx_contract_address = if tx_contract_opt.is_some() {
                        tx_contract_opt.unwrap()
                    } else {
                        continue;
                    };
                    if contract_str.eq_ignore_ascii_case(&tx_contract_address) {
                        let exists_ret = DAppTransaction::exists(common::CHAIN, &pool, &tx_id).await;
                        if let Ok(exists) = exists_ret {
                            if exists {
                                continue;
                            }
                        }
                        let tx_owner_address = parameter_value.owner_address.clone();
                        let tx_data_opt = parameter_value.data.clone();
                        let tx_decimals = 6;
                        let tx_data = if tx_data_opt.is_some() {
                            tx_data_opt.unwrap()
                        } else {
                            continue;
                        };
                        let method_id = &tx_data[..8];
                        let tx_data_len = tx_data.len();
                        for account_str in account_vec {
                            let account_hex = TronAddress::from_str(account_str).unwrap().to_hex();
                            let account_hex = account_hex.split_at(2).1;
                            match method_id {
                                METHOD_ID_TRANSFER => {
                                    if tx_data_len != 136 {
                                        continue;
                                    }
                                    let (address, amount) = parse_transfer_input(&tx_data);
                                    if address.eq_ignore_ascii_case(account_hex) {
                                        dapp_txs.push(DAppTransaction::from(
                                            String::from("TRON"),
                                            contract_str.to_string(),
                                            tx_owner_address.clone(),
                                            account_str.to_string(),
                                            tx_id.clone(),
                                            time_stamp.to_string(),
                                            next_number.to_string(),
                                            amount,
                                            tx_decimals,
                                        ));
                                    }
                                }
                                METHOD_ID_TRANSFER_FROM => {
                                    if tx_data_len != 200 {
                                        continue;
                                    }
                                    let (from, to, amount) = parse_transfer_from_input(&tx_data);
                                    if to.eq_ignore_ascii_case(account_hex) {
                                        let from_bs58 = TronAddress::from_str(from.as_str()).unwrap().to_base58();
                                        dapp_txs.push(DAppTransaction::from(
                                            String::from("TRON"),
                                            contract_str.to_string(),
                                            from_bs58,
                                            account_str.to_string(),
                                            tx_id.clone(),
                                            time_stamp.to_string(),
                                            next_number.to_string(),
                                            amount,
                                            tx_decimals,
                                        ));
                                    }
                                }
                                _ => {
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
        }
        next_number += 1;
    }
    
    let _ = DAppBlockScanProgress::update(pool, next_number - 1, scan_progress_id).await?;

    if dapp_txs.len() > 0 {
        let _ = DAppTransaction::insert_batch(&pool, &dapp_txs).await?;
    }

    Ok(())
}

fn parse_transfer_input(data: &str) -> (String, String) {
    let param_address = &data[8..72];
    let param_amount = &data[72..];

    let address = param_address.trim_start_matches('0').to_string();
    let amount = param_amount.trim_start_matches('0').to_string();
    let amount_u256 = U256::from_str_radix(amount.as_str(), 16).unwrap();
    let amount = amount_u256.to_string();
    (address, amount)
}

fn parse_transfer_from_input(data: &str) -> (String, String, String) {
    let param_from_address = &data[8..72];
    let param_to_address = &data[72..136];
    let param_amount = &data[136..];

    let from_address = param_from_address.trim_start_matches('0').to_string();
    let to_address = param_to_address.trim_start_matches('0').to_string();
    let amount = param_amount.trim_start_matches('0').to_string();
    let amount_u256 = U256::from_str_radix(amount.as_str(), 16).unwrap();
    let amount = amount_u256.to_string();
    (from_address, to_address, amount)
}

fn _encode_trim(data: &[u8]) -> String {
    let encoded = hex::encode(data);
    encoded.trim_start_matches('0').to_string()
}
